from PySide6.QtWidgets import (QLineEdit, QPushButton, QWidget, QLabel, QCompleter, QVBoxLayout,
                            QCheckBox, QComboBox, QSpinBox, QDoubleSpinBox, QDateEdit, QTextEdit, QFrame,
                            QHBoxLayout, QCalendarWidget, QTimeEdit)
from PySide6.QtCore import QDate, Signal, Qt, QModelIndex, QDateTime, QTimer, QPoint, QTime, QObject, QThread
from PySide6.QtGui import QPainter, QColor, QFont, QStandardItem, QStandardItemModel, QPixmap, QImage
from common import config
from datetime import datetime, time
import cv2
from pyzbar import pyzbar

class NormalLabel(QLabel):
    def __init__(self, text, parent=None):
        super().__init__(text, parent)

class WarningLabel(QLabel):
    def __init__(self, text, parent=None):
        super().__init__(text, parent)

class NormalLineEdit(QLineEdit):
    def __init__(self, parent=None):
        super().__init__(parent)

    def text(self):
        return super().text().strip()
    
class NormalTextEdit(QTextEdit):
    def __init__(self, parent=None, max_length=1000):
        super().__init__(parent)
        self.max_length = max_length
        self.textChanged.connect(self.limit_text)

    def text(self):
        return super().toPlainText()
    
    def limit_text(self):
        text = self.toPlainText()
        if len(text) > self.max_length:
            self.blockSignals(True)
            self.setPlainText(text[:self.max_length])
            self.moveCursor(self.textCursor().MoveOperation.End)
            self.blockSignals(False)
        self.update()  # 重新触发 paintEvent

    def paintEvent(self, event):
        # 先让父类正常绘制
        super().paintEvent(event)

        # 再自己绘制字数统计
        painter = QPainter(self.viewport())  # 画在编辑区域
        painter.setRenderHint(QPainter.RenderHint.Antialiasing)

        text = self.toPlainText()
        current_length = len(text)
        counter_text = f"{current_length}/{self.max_length}"

        # 颜色变化逻辑
        if current_length >= self.max_length:
            color = QColor("#ff4d4f")  # 红色，满了
        elif current_length > self.max_length * 0.8:
            color = QColor("#faad14")  # 橙色，接近满
        else:
            color = QColor("#bfbfbf")  # 灰色，正常

        painter.setPen(color)
        font = QFont()
        font.setPointSize(9)
        painter.setFont(font)

        # 计算位置（右下角）
        margin = 5
        metrics = painter.fontMetrics()
        text_width = metrics.horizontalAdvance(counter_text)
        text_height = metrics.height()

        rect = self.viewport().rect()
        x = rect.right() - text_width - margin
        y = rect.bottom() - margin

        painter.drawText(x, y, counter_text)
        painter.end()

class NormalComboBox(QComboBox):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.value_type = "list"

    def setItems(self, items):
        "支持字典类型,键是显示的文本，值是存储的值"
        self.blockSignals(True)
        if isinstance(items, dict):
            self.value_type = "dict"
            for k, v in items.items():
                self.addItem(k)
                self.setItemData(self.count()-1, v)
        elif isinstance(items, list):
            self.value_type = "list"
            for item in items:
                super().addItem(item)
        self.blockSignals(False)

    def currentData(self):
        "获取当前选中项的值"
        index = self.currentIndex()
        if index < 0:
            return None
        if self.value_type == "list":
            return self.itemText(index)
        else:
            return self.itemData(index)

class NormalFloatSpinBox(QDoubleSpinBox):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setRange(0, 99999)
        self.setDecimals(2)
        self.setSingleStep(0.1)

class NormalIntSpinBox(QSpinBox):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setRange(0, 99999)

class SearchLineEdit(QLineEdit):
    def __init__(self, parent=None, placeholder_text=""):
        super().__init__(parent)
        if placeholder_text:
            self.setPlaceholderText(placeholder_text)

class NormalCheckBox(QCheckBox):
    def __init__(self, parent=None, text=""):
        super().__init__(parent)
        self.setText(text)

class BluePushButton(QPushButton):
    def __init__(self, text, parent=None):
        super().__init__(text, parent)

class GreenPushButton(QPushButton):
    def __init__(self, text, parent=None):
        super().__init__(text, parent)

class WhitePushButton(QPushButton):
    def __init__(self, text, parent=None):
        super().__init__(text, parent)

class SearchPushButton(QPushButton):
    def __init__(self, text="搜索", parent=None):
        super().__init__(text, parent)

class MenuPushButton(QPushButton):
    def __init__(self, text, parent=None):
        super().__init__(text, parent)
        self.setCheckable(True)

class ChartPushButton(QPushButton):
    def __init__(self, text, parent=None):
        super().__init__(text, parent)
        self.setCheckable(True)

class SearchPanel(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

class NormalDateEdit(QDateEdit):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setDate(QDate.currentDate())
        self.setCalendarPopup(True)
        self.setDisplayFormat("yyyy-MM-dd")

#带自动补全的QLineEdit
class AutoCompleteLineEdit(QLineEdit):
    selectedChanged = Signal(object)
    onSubmit = Signal(object)
    def __init__(self, query_callback, placeholder_text="", parent=None, item_key="name"):
        """
        :param query_callback: 接收文本keyword，返回 {id: {data}} 列表
        """
        super().__init__(parent)

        self.query_callback = query_callback
        self.item_key = item_key
        self.selectedItem = None
        self.setPlaceholderText(placeholder_text)

        self.model = QStandardItemModel()
        self.completer = QCompleter(self.model, self)
        self.completer.setFilterMode(Qt.MatchContains)
        self.completer.setCompletionRole(Qt.DisplayRole)
        self.setCompleter(self.completer)
        popup = self.completer.popup()
        popup.setObjectName("completerPopup")

        # 信号
        self.textEdited.connect(self.on_text_edited)
        self.completer.activated[QModelIndex].connect(self.on_completer_activated)

    def on_completer_activated(self, index):
        self.selectedItem = index.data(Qt.UserRole)
        self.selectedChanged.emit(self.selectedItem)

    def on_text_edited(self, text):
        self.update_completer_by_query(text.strip())

    def update_completer_by_query(self, text):
        """根据输入文本动态查询并更新补全"""
        if self.query_callback:
            results = self.query_callback(text)
            self.set_items(results)

    def set_items(self, items):
        self.model.clear()
        self.selectedItem = None
        for item in items:
            if self.item_key not in item:
                continue
            item_ = QStandardItem(item[self.item_key])
            item_.setData(item, Qt.UserRole)
            self.model.appendRow(item_)

    def set_item(self, text, item):
        self.setText(text)
        self.selectedItem = item

    def get_selected_item(self):
        """返回当前文本对应的value"""
        return self.selectedItem

    def set_text_and_update(self, text):
        """外部设置文本并自动刷新对应补全"""
        self.setText(text)
        self.update_completer_by_query(text)

    def keyReleaseEvent(self, event):
        if event.key() == Qt.Key.Key_Enter and self.selectedItem:
            self.onSubmit.emit(self.selectedItem)
        return super().keyReleaseEvent(event)

class DateRangePopup(QFrame):
    rangeSelected = Signal(object, object)

    def __init__(self, parent=None, with_time=False):
        super().__init__(parent, Qt.Popup)
        self.setWindowFlags(Qt.Popup | Qt.FramelessWindowHint)
        self.setObjectName("dateRangePopup")
        self.with_time = with_time

        layout = QVBoxLayout(self)
        layout.setContentsMargins(16, 16, 16, 16)
        layout.setSpacing(8)

        # 日期选择区域
        date_widget = QWidget()
        date_layout = QHBoxLayout(date_widget)
        date_layout.setContentsMargins(0, 0, 0, 0)
        date_layout.setSpacing(16)

        # 左侧日历容器
        left_calendar = QWidget()
        left_calendar.setObjectName("leftCalendar")
        left_layout = QVBoxLayout(left_calendar)
        left_layout.setContentsMargins(8, 8, 8, 8)
        left_layout.setSpacing(4)

        # 左侧月份导航
        left_nav = QHBoxLayout()
        self.left_prev_year = QPushButton("<<")
        self.left_prev_month = QPushButton("<")
        self.left_month_label = QLabel()
        self.left_month_label.setObjectName("monthLabel")
        self.left_next_month = QPushButton(">")
        self.left_next_year = QPushButton(">>")
        
        for btn in [self.left_prev_year, self.left_prev_month, 
                   self.left_next_month, self.left_next_year]:
            btn.setObjectName("navBtn")
            
        self.left_month_label.setAlignment(Qt.AlignCenter)
        
        left_nav.addWidget(self.left_prev_year)
        left_nav.addWidget(self.left_prev_month)
        left_nav.addWidget(self.left_month_label, 1)
        left_nav.addWidget(self.left_next_month)
        left_nav.addWidget(self.left_next_year)
        
        # 日历表格
        self.left_calendar = QCalendarWidget()
        self.left_calendar.setVerticalHeaderFormat(QCalendarWidget.NoVerticalHeader)
        self.left_calendar.setHorizontalHeaderFormat(QCalendarWidget.SingleLetterDayNames)
        self.left_calendar.setNavigationBarVisible(False)
        self.left_calendar.setFixedSize(280, 240)
        
        left_layout.addLayout(left_nav)
        left_layout.addWidget(self.left_calendar)

        # 右侧日历容器
        right_calendar = QWidget()
        right_calendar.setObjectName("rightCalendar")
        right_layout = QVBoxLayout(right_calendar)
        right_layout.setContentsMargins(8, 8, 8, 8)
        right_layout.setSpacing(4)

        # 右侧月份导航
        right_nav = QHBoxLayout()
        self.right_prev_year = QPushButton("<<")
        self.right_prev_month = QPushButton("<")
        self.right_month_label = QLabel()
        self.right_month_label.setObjectName("monthLabel")
        self.right_next_month = QPushButton(">")
        self.right_next_year = QPushButton(">>")
        
        for btn in [self.right_prev_year, self.right_prev_month, 
                   self.right_next_month, self.right_next_year]:
            btn.setObjectName("navBtn")
            
        self.right_month_label.setAlignment(Qt.AlignCenter)
        
        right_nav.addWidget(self.right_prev_year)
        right_nav.addWidget(self.right_prev_month)
        right_nav.addWidget(self.right_month_label, 1)
        right_nav.addWidget(self.right_next_month)
        right_nav.addWidget(self.right_next_year)
        
        self.right_calendar = QCalendarWidget()
        self.right_calendar.setVerticalHeaderFormat(QCalendarWidget.NoVerticalHeader)
        self.right_calendar.setHorizontalHeaderFormat(QCalendarWidget.SingleLetterDayNames)
        self.right_calendar.setNavigationBarVisible(False)
        self.right_calendar.setFixedSize(280, 240)
        
        right_layout.addLayout(right_nav)
        right_layout.addWidget(self.right_calendar)

        date_layout.addWidget(left_calendar)
        date_layout.addWidget(right_calendar)
        
        layout.addWidget(date_widget)

        # 时间选择区域（可选）
        if with_time:
            time_widget = QWidget()
            time_layout = QHBoxLayout(time_widget)
            time_layout.setContentsMargins(0, 0, 0, 0)
            time_layout.setSpacing(16)

            # 开始时间
            start_time_layout = QHBoxLayout()
            start_time_layout.addWidget(QLabel("开始时间:"))
            self.start_time = QTimeEdit()
            self.start_time.setDisplayFormat("HH:mm:ss")
            start_time_layout.addWidget(self.start_time)
            
            # 结束时间
            end_time_layout = QHBoxLayout()
            end_time_layout.addWidget(QLabel("结束时间:"))
            self.end_time = QTimeEdit()
            self.end_time.setDisplayFormat("HH:mm:ss")
            end_time_layout.addWidget(self.end_time)

            time_layout.addLayout(start_time_layout)
            time_layout.addLayout(end_time_layout)
            
            layout.addWidget(time_widget)

        # 快捷选择按钮
        shortcut_widget = QWidget()
        shortcut_layout = QHBoxLayout(shortcut_widget)
        shortcut_layout.setContentsMargins(0, 0, 0, 0)
        shortcut_layout.setSpacing(8)
        
        shortcuts = [
            ("今天", self.select_today),
            ("本周", self.select_this_week),
            ("本月", self.select_this_month),
            ("本年", self.select_this_year),
            ("不限", self.select_unlimited)
        ]
        
        for text, callback in shortcuts:
            btn = QPushButton(text)
            btn.setObjectName("shortcutBtn")
            btn.clicked.connect(callback)
            shortcut_layout.addWidget(btn)
        
        #shortcut_layout.addStretch()  # 添加弹性空间，使按钮左对齐
        layout.addWidget(shortcut_widget)

        # 底部按钮
        btn_layout = QHBoxLayout()
        btn_layout.setSpacing(8)
        
        btn_cancel = QPushButton("取消")
        btn_cancel.setObjectName("cancelBtn")
        btn_ok = QPushButton("确定")
        btn_ok.setObjectName("okBtn")

        btn_cancel.clicked.connect(self.hide)
        btn_ok.clicked.connect(self.emit_result)

        btn_layout.addWidget(btn_cancel)
        btn_layout.addStretch()
        btn_layout.addWidget(btn_ok)

        layout.addLayout(btn_layout)

        # 连接信号
        self._connect_signals()
        self._update_month_labels()

    def _connect_signals(self):
        # 左侧日历导航
        self.left_prev_year.clicked.connect(
            lambda: self.left_calendar.setSelectedDate(
                self.left_calendar.selectedDate().addYears(-1)))
        self.left_prev_month.clicked.connect(
            lambda: self.left_calendar.setSelectedDate(
                self.left_calendar.selectedDate().addMonths(-1)))
        self.left_next_month.clicked.connect(
            lambda: self.left_calendar.setSelectedDate(
                self.left_calendar.selectedDate().addMonths(1)))
        self.left_next_year.clicked.connect(
            lambda: self.left_calendar.setSelectedDate(
                self.left_calendar.selectedDate().addYears(1)))
        
        # 右侧日历导航
        self.right_prev_year.clicked.connect(
            lambda: self.right_calendar.setSelectedDate(
                self.right_calendar.selectedDate().addYears(-1)))
        self.right_prev_month.clicked.connect(
            lambda: self.right_calendar.setSelectedDate(
                self.right_calendar.selectedDate().addMonths(-1)))
        self.right_next_month.clicked.connect(
            lambda: self.right_calendar.setSelectedDate(
                self.right_calendar.selectedDate().addMonths(1)))
        self.right_next_year.clicked.connect(
            lambda: self.right_calendar.setSelectedDate(
                self.right_calendar.selectedDate().addYears(1)))
        
        # 日期选择变化时更新标签
        self.left_calendar.selectionChanged.connect(self._update_month_labels)
        self.right_calendar.selectionChanged.connect(self._update_month_labels)

    def _update_month_labels(self):
        left_date = self.left_calendar.selectedDate()
        right_date = self.right_calendar.selectedDate()
        
        self.left_month_label.setText(
            left_date.toString("yyyy年MM月"))
        self.right_month_label.setText(
            right_date.toString("yyyy年MM月"))

    def select_today(self):
        today = QDate.currentDate()
        self.left_calendar.setSelectedDate(today)
        self.right_calendar.setSelectedDate(today)
        if self.with_time:
            self.start_time.setTime(QTime(0, 0))
            self.end_time.setTime(QTime(23, 59, 59))
        self.emit_result()

    def select_this_week(self):
        today = QDate.currentDate()
        monday = today.addDays(-(today.dayOfWeek() - 1))
        sunday = monday.addDays(6)
        self.left_calendar.setSelectedDate(monday)
        self.right_calendar.setSelectedDate(sunday)
        if self.with_time:
            self.start_time.setTime(QTime(0, 0))
            self.end_time.setTime(QTime(23, 59, 59))
        self.emit_result()

    def select_this_month(self):
        today = QDate.currentDate()
        first_day = QDate(today.year(), today.month(), 1)
        last_day = QDate(today.year(), today.month(), today.daysInMonth())
        self.left_calendar.setSelectedDate(first_day)
        self.right_calendar.setSelectedDate(last_day)
        if self.with_time:
            self.start_time.setTime(QTime(0, 0))
            self.end_time.setTime(QTime(23, 59, 59))
        self.emit_result()

    def select_this_year(self):
        today = QDate.currentDate()
        first_day = QDate(today.year(), 1, 1)
        last_day = QDate(today.year(), 12, 31)
        self.left_calendar.setSelectedDate(first_day)
        self.right_calendar.setSelectedDate(last_day)
        if self.with_time:
            self.start_time.setTime(QTime(0, 0))
            self.end_time.setTime(QTime(23, 59, 59))
        self.emit_result()

    def select_unlimited(self):
        self.left_calendar.setSelectedDate(QDate.currentDate())
        self.right_calendar.setSelectedDate(QDate.currentDate())
        self.rangeSelected.emit(None, None)
        self.hide()

    def emit_result(self):
        start_date = self.left_calendar.selectedDate()
        end_date = self.right_calendar.selectedDate()
        
        if self.with_time:
            start_time = self.start_time.time()
            end_time = self.end_time.time()
            start_dt = datetime.combine(start_date.toPython(), start_time.toPython())
            end_dt = datetime.combine(end_date.toPython(), end_time.toPython())
        else:
            start_dt = datetime.combine(start_date.toPython(), time(0, 0))
            end_dt = datetime.combine(end_date.toPython(), time(23, 59, 59))
            
        if start_dt > end_dt:
            start_dt, end_dt = end_dt, start_dt
            
        self.rangeSelected.emit(start_dt, end_dt)
        self.hide()

    def set_initial_range(self, start: datetime, end: datetime):
        self.left_calendar.setSelectedDate(QDate(start))
        self.right_calendar.setSelectedDate(QDate(end))
        if self.with_time:
            self.start_time.setTime(QTime(start.hour, start.minute, start.second))
            self.end_time.setTime(QTime(end.hour, end.minute, end.second))

class DateRangeEdit(QLineEdit):
    onRangeChanged = Signal(object, object)

    def __init__(self, parent=None, with_time=False, placeholder_text="请选择时间区间"):
        super().__init__(parent)
        self.setReadOnly(True)
        self.setPlaceholderText(placeholder_text)
        self.setMinimumWidth(300)
        self.with_time = with_time
        self.setProperty("with_time", with_time)
        self.start_time = None
        self.end_time = None

        self.popup = DateRangePopup(self, with_time)
        self.popup.rangeSelected.connect(self._on_range_selected)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.show_popup()
        super().mousePressEvent(event)

    def show_popup(self):
        self.popup.set_initial_range(self.start_time if self.start_time else datetime.now(),
                                     self.end_time if self.end_time else datetime.now())
        
        # 获取主窗口和当前控件的几何信息
        main_window = self.window()
        main_geo = main_window.geometry()
        global_pos = self.mapToGlobal(QPoint(0, self.height()))
        popup_size = self.popup.sizeHint()
        
        # 计算弹出窗口的理想位置
        pos_x = global_pos.x()
        pos_y = global_pos.y()
        
        # 检查右边界
        if pos_x + popup_size.width() > main_geo.right():
            pos_x = main_geo.right() - popup_size.width()
        
        # 检查左边界
        if pos_x < main_geo.left():
            pos_x = main_geo.left()
            
        # 检查下边界
        if pos_y + popup_size.height() > main_geo.bottom():
            # 如果下方空间不足，则在控件上方显示
            pos_y = global_pos.y() - popup_size.height() - self.height()
        
        # 检查上边界
        if pos_y < main_geo.top():
            pos_y = main_geo.top()
        
        self.popup.move(pos_x, pos_y)
        self.popup.show()

    def _on_range_selected(self, start, end):
        if (not start and not end):
            self.setText("")
            self.start_time = None
            self.end_time = None
        else:
            if self.with_time:
                text = f"{start.strftime('%Y-%m-%d %H:%M:%S') if start else ''} ~ {end.strftime('%Y-%m-%d %H:%M:%S' if end else '')}"
            else:
                text = f"{start.strftime('%Y-%m-%d') if start else ''} ~ {end.strftime('%Y-%m-%d') if end else ''}"
            self.setText(text)
            self.start_time = start
            self.end_time = end
        self.onRangeChanged.emit(start, end)

    def get_range(self):
        return (self.start_time, self.end_time)

WEEK_MAP = {
    "Mon": "星期一",
    "Tue": "星期二",
    "Wed": "星期三",
    "Thu": "星期四",
    "Fri": "星期五",
    "Sat": "星期六",
    "Sun": "星期日"
}

class TimeLabel(QLabel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.update_time()
        self._timer = QTimer(self)
        self._timer.timeout.connect(self.update_time)
        self._timer.start(1000)

    def update_time(self):
        now = QDateTime.currentDateTime()
        self.setText(f"{now.toString('yyyy-MM-dd')} {WEEK_MAP[now.toString('ddd')]} {now.toString('HH:mm:ss')}")

# 摄像头管理
class CameraManager(QObject):
    _instance = None
    _initialized = False

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super(CameraManager, cls).__new__(cls)
        return cls._instance

    def __init__(self):
        if not self._initialized:
            super().__init__()
            self.cap = None
            CameraManager._initialized = True

    def initialize(self):
        if self.cap is None or not self.cap.isOpened():
            self.cap = cv2.VideoCapture(0)
        return self.cap.isOpened()

    def frame(self):
        if not self.cap or not self.cap.isOpened():
            return
        while self.cap.isOpened():
            ret, frame = self.cap.read()
            if not ret:
                break
            yield frame

    def release(self):
        if self.cap and self.cap.isOpened():
            self.cap.release()
            self.cap = None

Camera = CameraManager()

# 后台线程：初始化摄像头
class CameraInitThread(QThread):
    result = Signal(bool)

    def run(self):
        ok = Camera.initialize()
        self.result.emit(ok)

    def safe_stop(self):
        Camera.release()


# 后台线程：读取摄像头帧并扫码
class FrameLoopThread(QThread):
    frame_ready = Signal(QImage)
    qr_scanned = Signal(str)
    start_cooldown = Signal(int)  # 新增信号

    def __init__(self):
        super().__init__()
        self._cooldown = config.QR_SCAN_COOLDOWN
        self._can_scan = True
        self._running = True
        self.start_cooldown.connect(self._handle_cooldown)

    def _enable_scan(self):
        self._can_scan = True

    def _handle_cooldown(self, duration):
        self._can_scan = False
        QTimer.singleShot(duration, self._enable_scan)

    def pause(self):
        self._can_scan = False

    def resume(self):
        self._can_scan = True

    def run(self):
        for frame in Camera.frame():
            if not self._running:
                break

            rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            h, w, ch = rgb.shape
            qimg = QImage(rgb.data, w, h, ch * w, QImage.Format_RGB888)
            self.frame_ready.emit(qimg)

            if self._can_scan:
                decoded = pyzbar.decode(frame)
                if decoded:
                    result = decoded[0].data.decode("utf-8")
                    self.qr_scanned.emit(result)
                    self.start_cooldown.emit(self._cooldown * 1000)

            QThread.msleep(30)

    def stop(self):
        self._running = False
        self.start_cooldown.disconnect()

# 自定义扫码标签
class ScanLabel(QLabel):
    scan_result = Signal(str)

    def __init__(self, parent=None, size=None):
        super().__init__(parent)
        self.setText("摄像头初始化中…")
        self.setAlignment(Qt.AlignCenter)
        self._frame_thread = None
        if size:
            self.setFixedSize(*size)
        self.destroyed.connect(self.stop)

    def start(self):
        self._frame_thread = FrameLoopThread()
        self.setText("扫码中…")
        self._frame_thread.frame_ready.connect(self._on_frame_ready)
        self._frame_thread.qr_scanned.connect(self.scan_result)
        self._frame_thread.start()

    def _on_frame_ready(self, qimg: QImage):
        pixmap = QPixmap.fromImage(qimg).scaled(self.size(), Qt.KeepAspectRatio)
        self.setPixmap(pixmap)

    def pause(self):
        if self._frame_thread:
            self._frame_thread.pause()

    def resume(self):
        if self._frame_thread:
            self._frame_thread.resume()

    def stop(self):
        if self._frame_thread:
            try:
                self._frame_thread.frame_ready.disconnect()
                self._frame_thread.qr_scanned.disconnect()
            except:
                pass
            self._frame_thread.stop()
            self._frame_thread.wait()
            self._frame_thread = None
